package fr.jade.fraclite.control.binding;

import java.util.HashMap;
import java.util.Map;

import org.objectweb.fractal.api.Component;
import org.objectweb.fractal.api.Interface;
import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.api.control.BindingController;
import org.objectweb.fractal.api.control.ContentController;
import org.objectweb.fractal.api.control.IllegalBindingException;
import org.objectweb.fractal.api.control.IllegalLifeCycleException;
import org.objectweb.fractal.api.factory.InstantiationException;
import org.objectweb.fractal.api.type.ComponentType;
import org.objectweb.fractal.api.type.InterfaceType;

import fr.jade.fraclite.InitializationContext;
import fr.jade.fraclite.api.ComponentInterface;
import fr.jade.fraclite.api.Controller;

/**
 * Provides a basic implementation of the {@link BindingController} interface.
 * This mixin uses a map to store the bindings of the component. <br>
 * <br>
 * <b>Requirements</b>
 * <ul>
 * <li>none.</li>
 * </ul>
 */

public class ModulesBindingController implements Controller, BindingController {

	/**
	 * The bindings of the component to which this controller object belongs.
	 * This map associates to each client interface name the server interface to
	 * which it is bound. If a client interface is not bound, its name is not
	 * associated to <tt>null</tt>, but to the {@link #fcBindings} map
	 * itself. This way the {@link #listFc listFc} method returns the names of
	 * all the client interfaces of the component, and not only the names of the
	 * interfaces that are bound.
	 */

	protected Map<String, Object> fcBindings = null;

	protected BindingController innerImpl = null;

	protected Component weaveableC = null;

	protected ContentController weaveableCC = null;

	public void initFcController(InitializationContext ic)
			throws InstantiationException {
		weaveableC = (Component) ic.getInterface("component");

		if (ic.content == null) {
			fcBindings = new HashMap<String, Object>();
			weaveableCC = (ContentController) ic
					.getInterface("content-controller");

			ComponentType compType = (ComponentType) ic.type;
			InterfaceType[] itfTypes = compType.getFcInterfaceTypes();
			boolean isComposite = ic.getOptionalInterface("content-controller") != null;

			// initializes the fcBindings map with empty bindings for all the
			// interfaces in the component's type
			for (int j = 0; j < itfTypes.length; j++) {
				InterfaceType itfType = itfTypes[j];
				if (!itfType.isFcCollectionItf()
						&& !itfType.getFcItfName().equals("component")
						&& !itfType.getFcItfName().endsWith("-controller")
						&& (itfType.isFcClientItf() || isComposite)) {

					fcBindings.put(itfType.getFcItfName(), fcBindings);
				}
			}

		}
		if (ic.content instanceof BindingController) {
			innerImpl = (BindingController) ic.content;
		}
	}

	public String[] listFc() {
		if (innerImpl != null) {
			return innerImpl.listFc();
		} else if (fcBindings != null) {
			return (String[]) fcBindings.keySet().toArray(
					new String[fcBindings.size()]);
		}
		return new String[] {};
	}

	public Object lookupFc(final String clientItfName)
			throws NoSuchInterfaceException {
		if (innerImpl != null) {
			return innerImpl.lookupFc(clientItfName);
		} else {
			Object serverItf = fcBindings.get(clientItfName);
			return serverItf == fcBindings ? null : serverItf;
		}
	}

	public void bindFc(final String clientItfName, final Object serverItf)
			throws NoSuchInterfaceException, IllegalBindingException,
			IllegalLifeCycleException {
		if (innerImpl != null) {
			innerImpl.bindFc(clientItfName, serverItf);
		} else {
			if(fcBindings == null) {
				fcBindings = new HashMap<String, Object>();
			}
			fcBindings.put(clientItfName, serverItf);
			updateInterfaceImpl(clientItfName, serverItf);
		}
	}

	public void unbindFc(final String clientItfName)
			throws NoSuchInterfaceException, IllegalBindingException,
			IllegalLifeCycleException {
		if (innerImpl != null) {
			innerImpl.unbindFc(clientItfName);
		} else {
			fcBindings.put(clientItfName, fcBindings);
			updateInterfaceImpl(clientItfName, null);
		}
	}

	protected void updateInterfaceImpl(String clientItfName, Object serverItf)
			throws NoSuchInterfaceException {
		Interface itf = (Interface) weaveableC.getFcInterface(clientItfName);
		InterfaceType clientItfType = (InterfaceType) itf.getFcItfType();
		ComponentInterface complementaryItf = null;
		if (clientItfType.isFcClientItf() && weaveableCC != null) {
			complementaryItf = (ComponentInterface) weaveableCC
					.getFcInternalInterface(clientItfName);
		} else {
			complementaryItf = (ComponentInterface) weaveableC
					.getFcInterface(clientItfName);
		}
		((ComponentInterface) complementaryItf).setFcItfImpl(serverItf);
	}

}
